package wangjg.commons.jmx;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.DynamicMBean;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.modelmbean.ModelMBeanAttributeInfo;
import javax.management.modelmbean.ModelMBeanOperationInfo;

public class MyDynamicMBean implements DynamicMBean {
	private Object bean;
	private boolean readOnly;
	
	public MyDynamicMBean(Object bean) {
		this(bean,true);
	}
	
	public MyDynamicMBean(Object bean,boolean readOnly) {
		this.bean = bean;
		this.readOnly = readOnly;
	}
	
	public MBeanInfo getMBeanInfo() {
		try {
			MBeanInfo info = new MBeanInfo(this.getBeanClass().getName(),"...",this.getMBeanAttributeInfos(),this.getMBeanConstructorInfos(),this.getMBeanOperationInfos(), null);
			return info;
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}
	
	private Class getBeanClass(){
		return this.bean.getClass();
	}

	private MBeanAttributeInfo[] getMBeanAttributeInfos() throws Exception {
		ArrayList<MBeanAttributeInfo> alist=new ArrayList<MBeanAttributeInfo>();
		Class<?> c = this.getBeanClass();
	    BeanInfo beanInfo = Introspector.getBeanInfo(c);
	    PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
	    for(PropertyDescriptor p:pds){
	    	Method readMethod=p.getReadMethod();
	    	Method writeMethod=p.getWriteMethod();
	    	if(readOnly)writeMethod=null;
	    	
	    	ModelMBeanAttributeInfo attr = new ModelMBeanAttributeInfo(p.getName(), p.getShortDescription(), readMethod,writeMethod);
	    	alist.add(attr);
	    }
	    MBeanAttributeInfo[] as = new MBeanAttributeInfo[alist.size()];
	    alist.toArray(as);
	    return as;
	}
	
	private MBeanOperationInfo[] getMBeanOperationInfos() throws Exception{
		ArrayList<MBeanOperationInfo> mlist=new ArrayList<MBeanOperationInfo>();
		
		Class<?> c = this.getBeanClass();
	    BeanInfo beanInfo = Introspector.getBeanInfo(c);
	    MethodDescriptor[] pds = beanInfo.getMethodDescriptors();
	    for(MethodDescriptor p:pds){
			ModelMBeanOperationInfo moi = new ModelMBeanOperationInfo(p.getShortDescription(), p.getMethod());
			mlist.add(moi);
	    }
	    MBeanOperationInfo[] ms = new MBeanOperationInfo[mlist.size()];
	    mlist.toArray(ms);
	    return ms;
	}
	
	private MBeanConstructorInfo[] getMBeanConstructorInfos(){
		ArrayList<MBeanConstructorInfo> clist=new ArrayList<MBeanConstructorInfo>();
		
		return null;
	}

	@Override
	public Object invoke(String actionName, Object[] params, String[] signature) {
		Class<?>[] paraClass = new Class[params.length];
		for (int i = 0; i < params.length; i++) {
			paraClass[i] = params[i].getClass();
		}
		try {
			Class<?> c = this.getBeanClass();
			Method m = c.getMethod(actionName, paraClass);
			return m.invoke(this.bean, params);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public Object getAttribute(String attribute) {
		Class cls = this.getBeanClass();
		try {
			String methodName = getMethodNameByField(attribute);
			Method GetMethod = cls.getMethod("get" + methodName, null);
			return GetMethod.invoke(this.bean, null);
		} catch (NoSuchMethodException e) {
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		try {
			Field f=cls.getField(attribute);
			f.setAccessible(true);
			return f.get(this.bean);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return null;
	}

	public void setAttribute(Attribute attribute) {
		if(readOnly)
			return;
		Class cls = this.getBeanClass();
		try {
			String methodName = getMethodNameByField(attribute.getName());
			Method SetMethod = cls.getMethod("set" + methodName,new Class[] { attribute.getValue().getClass() });
			SetMethod.invoke(this.bean, attribute.getValue());
			return;
		} catch (NoSuchMethodException e) {
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		try{
			Field f=cls.getField(attribute.getName());
			f.setAccessible(true);
			f.set(this.bean, attribute.getValue());
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}

	@Override
	public AttributeList getAttributes(String[] attributes) {
		
		return null;
	}

	@Override
	public AttributeList setAttributes(AttributeList attributes) {
		return null;
	}

	private String getMethodNameByField(String attribute) {
		String methodName = attribute.substring(0, 1).toUpperCase()
				+ attribute.substring(1, attribute.length());
		return methodName;
	}

}